package com.hzit.mmcb.core.service.impl;

import cn.hutool.core.lang.hash.KetamaHash;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.ram.model.v20150501.ListEntitiesForPolicyRequest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hzit.common.exception.Assert;
import com.hzit.common.result.R;
import com.hzit.common.result.ResponseEnum;
import com.hzit.mmcb.core.enums.LendStatusEnum;
import com.hzit.mmcb.core.enums.ReturnMethodEnum;
import com.hzit.mmcb.core.enums.TransTypeEnum;
import com.hzit.mmcb.core.hfb.HfbConst;
import com.hzit.mmcb.core.hfb.RequestHelper;
import com.hzit.mmcb.core.pojo.dto.BorrowerDetailDTO;
import com.hzit.mmcb.core.pojo.entity.*;
import com.hzit.mmcb.core.mapper.LendMapper;
import com.hzit.mmcb.core.pojo.vo.BorrowInfoApprovalVO;
import com.hzit.mmcb.core.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzit.mmcb.core.utils.*;
import org.checkerframework.checker.units.qual.A;
import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.util.resources.cldr.fa.CalendarData_fa_IR;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author ljh
 * @since 2024-08-27
 */
@Service
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {
    @Autowired
    private BorrowerService borrowerService;
    @Autowired
    private UserAccountService userAccountService;
    @Autowired
    private TransFlowService transFlowService;
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private LendItemService lendItemService;
    @Autowired
    private LendReturnService lendReturnService;
    @Autowired
    private LendItemReturnService lendItemReturnService;
    // @Resource

    /**
     * 创建标的
     * @param vo        前端传来的表单信息
     * @param borrowInfo    借款信息
     */
    @Override
    public void createLend(BorrowInfoApprovalVO vo, BorrowInfo borrowInfo) {
        Lend lend = new Lend();
        lend.setUserId(borrowInfo.getUserId());
        lend.setBorrowInfoId(borrowInfo.getId());
        //生成标的码
        lend.setLendNo(LendNoUtils.getLendNo());
        lend.setTitle(vo.getTitle());
        lend.setAmount(borrowInfo.getAmount());
        lend.setPeriod(borrowInfo.getPeriod());
        lend.setLendYearRate(vo.getLendYearRate().divide(new BigDecimal(100)));
        lend.setServiceRate(vo.getServiceRate().divide(new BigDecimal(100)));
        lend.setReturnMethod(borrowInfo.getReturnMethod());
        lend.setLowestAmount(new BigDecimal(100));
        lend.setInvestAmount(new BigDecimal(0));
        lend.setInvestNum(0);
        lend.setPublishDate(LocalDateTime.now());
        //起息日期
        DateTimeFormatter ymd = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate parse = LocalDate.parse(vo.getLendStartDate(), ymd);
        lend.setLendStartDate(parse);
        //结束日期
        LocalDate localDate = parse.plusMonths(borrowInfo.getPeriod());
        lend.setLendEndDate(localDate);
        lend.setLendInfo(vo.getLendInfo());
        //月费率
        BigDecimal divide = lend.getServiceRate().divide(new BigDecimal(12), 8,BigDecimal.ROUND_DOWN);
        //预计收益
        BigDecimal expectAmount = lend.getAmount().multiply(divide).multiply(new BigDecimal(lend.getPeriod()));
        lend.setExpectAmount(expectAmount);
        lend.setRealAmount(new BigDecimal(0));
        //设置状态
        lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());
        //设置审核时间
        lend.setCheckTime(LocalDateTime.now());
        //审核人
        lend.setCheckAdminId(1L);
        baseMapper.insert(lend);
    }

    /**
     * 获取标的列表
     * @return 标的列表
     */
    @Override
    @SentinelResource(value = "lendList",blockHandler = "listNameBlock")
    public List<Lend> selectList() {
        List<Lend> list = this.list();
        list.forEach(lend -> {
            lend.getParam().put("status", LendStatusEnum.getMsgByStatus(lend.getStatus()));
            lend.getParam().put("returnMethod",borrowerService.getDictName(lend.getReturnMethod(),"returnMethod"));
        });
        return list;
    }

    /**
     * 查询标的的信息信息
     * @param id  标的id
     * @return  详细信息（标的信息、贷款人信息）
     */
    @Override
    public Map<String, Object> getLendDetail(Long id) {
        //查询标的信息
        Lend lend = this.getById(id);
        lend.getParam().put("status",LendStatusEnum.getMsgByStatus(lend.getStatus()));
        lend.getParam().put("returnMethod",borrowerService.getDictName(lend.getReturnMethod(),"returnMethod"));

        //查询贷款人信息
        Borrower borrowerServiceOne = borrowerService.getOne(new LambdaQueryWrapper<Borrower>().eq(Borrower::getUserId, lend.getUserId()));
        BorrowerDetailDTO borrower = borrowerService.getDetailById(borrowerServiceOne.getId().intValue());

        //将信息放入map
        Map<String,Object> map=new HashMap<>();
        map.put("lend",lend);
        map.put("borrower",borrower);
        return map;
    }

    /**
     * 放款
     * 向第三方托管平台发送信息
     * 修改标的信息
     * 修改借款人账户
     * 增加借款人交易流水（trans_flow)
     * 根据lendId及状态获取投资人列表
     * 修改投资人冻结资金
     * 增加冻结资金流水
     * 生成还款计划与回款计划
     * @param id        标的id（lendId)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void makeLoan(Long id) {
        //调用第三方平台的接口
        Map<String,Object> paramMap= new HashMap<>();
        paramMap.put("agentId", HfbConst.AGENT_ID);
        Lend lend = this.getById(id);
        paramMap.put("agentProjectCode",lend.getLendNo());
        String loanNo = LendNoUtils.getLoanNo();
        paramMap.put("agentBillNo",loanNo);
        //平台收益
        BigDecimal mothRate=lend.getServiceRate().divide(new BigDecimal(12),8,BigDecimal.ROUND_DOWN);
        BigDecimal realAmount = lend.getInvestAmount().multiply(mothRate).multiply(new BigDecimal(lend.getPeriod()));
        paramMap.put("mchFee",realAmount);
        // paramMap.put("note","");
        paramMap.put("timestamp", RequestHelper.getTimestamp());
        String sign = RequestHelper.getSign(paramMap);
        paramMap.put("sign",sign);
        JSONObject result = RequestHelper.sendRequest(paramMap, HfbConst.MAKE_LOAN_URL);
        Assert.isTrue("0000".equals(result.getString("resultCode")), ResponseEnum.NOT_MAKELOAN_FUNDS_ERROR);

        //修改标的信息
        lend.setRealAmount(realAmount);
        lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());
        lend.setPaymentTime(LocalDateTime.now());
        this.updateById(lend);

        //修改借款人账户信息
        BigDecimal voteAmt = new BigDecimal(result.getString("voteAmt"));
        UserAccount borrowerAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>().eq(UserAccount::getUserId, lend.getUserId()));
        borrowerAccount.setAmount(borrowerAccount.getAmount().add(voteAmt));
        userAccountService.updateById(borrowerAccount);

        UserInfo borrowerInfo = userInfoService.getById(lend.getUserId());
        //增加借款人交易流水
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(borrowerInfo.getId());
        transFlow.setUserName(borrowerInfo.getName());
        transFlow.setTransNo(loanNo);
        transFlow.setTransType(TransTypeEnum.BORROW_BACK.getIntegral());
        transFlow.setTransTypeName(TransTypeEnum.BORROW_BACK.getMsg());
        transFlow.setTransAmount(voteAmt);
        transFlow.setMemo("借款放款到账，编号：" + lend.getLendNo());
        transFlowService.save(transFlow);

        //根据lendId及状态获取投资人列表
        List<LendItem> lendItemList = lendItemService.list(new LambdaQueryWrapper<LendItem>()
                .eq(LendItem::getLendId, id)
                .eq(LendItem::getStatus, 1));
            //将投资人账户的冻结资金减去投资金额
        for (LendItem lendItem : lendItemList) {
            UserAccount itemAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>()
                    .eq(UserAccount::getUserId, lendItem.getInvestUserId()));
            // Assert.isTrue(itemAccount.getFreezeAmount().subtract(lendItem.getInvestAmount()).doubleValue()>0,ResponseEnum.NOT_MAKELOAN_FUNDS_ERROR);
            itemAccount.setFreezeAmount(itemAccount.getFreezeAmount().subtract(lendItem.getInvestAmount()));
            //增加冻结资金流水
            TransFlow flow = new TransFlow();

            flow.setUserId(lendItem.getInvestUserId());
            flow.setUserName(lendItem.getInvestName());
            String transNo = LendNoUtils.getTransNo();
            flow.setTransNo(transNo);
            flow.setTransType(TransTypeEnum.INVEST_UNLOCK.getIntegral());
            flow.setTransTypeName(TransTypeEnum.INVEST_UNLOCK.getMsg());
            flow.setTransAmount(voteAmt);
            flow.setMemo("借款放款到账，编号：" + lend.getLendNo());
            transFlowService.save(flow);
        }


        //生成还款计划与汇款计划
        //生成还款计划
        List<LendReturn> lendReturnList=new ArrayList<>();
        for (int i=1;i<=lend.getPeriod();i++){
            LendReturn lendReturn = new LendReturn();
            lendReturn.setLendId(lend.getId());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            lendReturn.setReturnNo(LendNoUtils.getReturnNo());
            lendReturn.setUserId(lend.getUserId());
            lendReturn.setAmount(lend.getAmount());
            lendReturn.setReturnMethod(lend.getReturnMethod());
            lendReturn.setBaseAmount(lend.getInvestAmount());
            lendReturn.setCurrentPeriod(i);
            lendReturn.setLendYearRate(lend.getLendYearRate());
            lendReturn.setFee(new BigDecimal(0));
            lendReturn.setReturnDate(lend.getLendStartDate().plusMonths(i));
            lendReturn.setOverdue(false);
            if (i==lend.getPeriod()){
                lendReturn.setLast(true);
            }else {
                lendReturn.setLast(false);
            }
            lendReturn.setStatus(0);
            lendReturnList.add(lendReturn);
        }
        lendReturnService.saveBatch(lendReturnList);

        //生成回款计划
        //得到期数对于的id
        Map<Integer, Long> collect = lendReturnList.stream().collect(Collectors.toMap(LendReturn::getCurrentPeriod, LendReturn::getId));
        List<LendItemReturn> lendItemReturnList=new ArrayList<>();
        for (LendItem lendItem : lendItemList) {
            List<LendItemReturn> lendItemReturns=this.returnInvest(lendItem,collect,lend);
            lendItemReturnList.addAll(lendItemReturns);
        }
        lendItemReturnService.saveBatch(lendItemReturnList);

        //修改回款计划中的本金，利息，本息

        for (LendReturn lendReturn : lendReturnList) {
            BigDecimal sumPrincipal = lendItemReturnList.stream().filter(item -> lendReturn.getId().longValue() == item.getLendReturnId().longValue())
                    .map(LendItemReturn::getPrincipal).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal sumInterest = lendItemReturnList.stream().filter(item -> lendReturn.getId().longValue() == item.getLendReturnId().longValue())
                    .map(LendItemReturn::getInterest)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            lendReturn.setPrincipal(sumPrincipal);
            lendReturn.setInterest(sumInterest);
            lendReturn.setTotal(sumInterest.add(sumPrincipal));
        }
        lendReturnService.updateBatchById(lendReturnList);

    }

    /**
     * 生成某个投资人的回款计划
     * @param lendItem
     * @param collect
     * @param lend
     * @return
     */
    private List<LendItemReturn> returnInvest(LendItem lendItem, Map<Integer, Long> collect, Lend lend) {
        Set<Integer> set = collect.keySet();
        //得到利息、本金、本息
        Map<Integer, BigDecimal> perMonthInterest = null;
        Map<Integer, BigDecimal> perMonthPrincipal = null;
        if (lend.getReturnMethod()== ReturnMethodEnum.ONE.getIntegral()){
            perMonthInterest = Amount1Helper.getPerMonthInterest(lendItem.getInvestAmount(), lendItem.getLendYearRate(), lend.getPeriod());
            perMonthPrincipal = Amount1Helper.getPerMonthPrincipal(lendItem.getInvestAmount(), lendItem.getLendYearRate(), lend.getPeriod());
        }else if (lend.getReturnMethod()== ReturnMethodEnum.TWO.getIntegral()){
            perMonthInterest = Amount2Helper.getPerMonthInterest(lendItem.getInvestAmount(), lendItem.getLendYearRate(), lend.getPeriod());
            perMonthPrincipal = Amount2Helper.getPerMonthPrincipal(lendItem.getInvestAmount(), lendItem.getLendYearRate(), lend.getPeriod());
        }else if (lend.getReturnMethod()== ReturnMethodEnum.THREE.getIntegral()){
            perMonthInterest = Amount3Helper.getPerMonthInterest(lendItem.getInvestAmount(), lendItem.getLendYearRate(), lend.getPeriod());
            perMonthPrincipal = Amount3Helper.getPerMonthPrincipal(lendItem.getInvestAmount(), lendItem.getLendYearRate(), lend.getPeriod());
        }else if (lend.getReturnMethod()== ReturnMethodEnum.FOUR.getIntegral()){
            perMonthInterest = Amount4Helper.getPerMonthInterest(lendItem.getInvestAmount(), lendItem.getLendYearRate(), lend.getPeriod());
            perMonthPrincipal = Amount4Helper.getPerMonthPrincipal(lendItem.getInvestAmount(), lendItem.getLendYearRate(), lend.getPeriod());
        }

        //生成该用户的回款列表
        List<LendItemReturn> lendItemReturnList=new ArrayList<>();
        for (int key:set) {
            LendItemReturn lendItemReturn = new LendItemReturn();
            lendItemReturn.setLendReturnId(collect.get(key));
            lendItemReturn.setLendId(lend.getId());
            lendItemReturn.setLendItemId(lendItem.getId());
            lendItemReturn.setInvestUserId(lendItem.getInvestUserId());
            lendItemReturn.setInvestAmount(lendItem.getInvestAmount());
            lendItemReturn.setCurrentPeriod(key);
            lendItemReturn.setLendYearRate(lend.getLendYearRate());
            lendItemReturn.setReturnMethod(lend.getReturnMethod());
            //判断是否是最后一期
            if (lendItemReturnList.size() > 0 && key==lend.getPeriod().intValue()){
                BigDecimal sumPrincipal = lendItemReturnList.stream()
                        .map(LendItemReturn::getPrincipal)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                lendItemReturn.setPrincipal(lendItem.getInvestAmount().subtract(sumPrincipal));
                lendItemReturn.setInterest(new BigDecimal(0));
            }else {
                lendItemReturn.setPrincipal(perMonthPrincipal.get(key));
                lendItemReturn.setInterest(perMonthInterest.get(key));
            }
            lendItemReturn.setTotal(perMonthPrincipal.get(key).add(perMonthInterest.get(key)));
            lendItemReturn.setFee(new BigDecimal(0));
            lendItemReturn.setReturnDate(lend.getLendStartDate().plusMonths(key));
            lendItemReturn.setOverdue(false);
            lendItemReturn.setStatus(0);
            lendItemReturnList.add(lendItemReturn);
        }
        return lendItemReturnList;
    }

    /**
     * 查询限制的流量
     */
    public List<Lend> listNameBlock(BlockException ex){
        List<Lend> lends=new ArrayList<>();
        Lend lend = new Lend();
        lend.getParam().put("status","限流了");
        lends.add(lend);
        return lends;
    }
}
